﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_INPUT_SYSTEM_H
#define _UXS_INPUT_SYSTEM_H

#pragma once

#include "../OS.h"

namespace UX_STL
{
namespace UXTypesExt
{
	using namespace UXTypesExt;
	using namespace UXMath;
	

	// Input Configure Flags
	namespace e_input_flags
	{
		enum	type	: uint16
		{
			UNKNOWN			= 0,
			EXCLUSIVE		= 0x0001,
			HIDE_CURSOR		= 0x0002,
			//LOCK_CURSOR		= 0x0004,
		};

		UX_ENUM_BIT_OPERATIONS( type )
	}
	

	// Key State
	namespace e_key_state
	{
		enum	type	: uint8
		{
			NOT_PRESSED	= 0x0,
			UP			= 0x1,	// кнопка только что отпущена
			DOWN		= 0x3,	// нажата
			PRESSED		= 0x4,
		};

		UX_ENUM_BIT_OPERATIONS( type )
	}



	//
	//	Input System
	//

	class UX_EXPORT CInput : public IWindowEventListener
	{
#	ifndef UX_MT_INPUT_DISABLE
		
	private:
		array< TWindowEvent >	_aEvents;
		Mutex		_sCSLockInput;

#	endif	// UX_MT_INPUT_DISABLE

	public:
		enum e_text_input_mode : uint8
		{
			TIM_NONE	= 0,
			TIM_ASCII	= 0x1,
			TIM_UNICODE	= 0x2,
		};

		typedef array< e_key::type, TFastCopyStrategy< e_key::type > >		key_array_t;

	private:
		Ptr<CWindow>		_pWindow;
		string				_sText;
		wstring				_sWText;
		ivec2				_sMPos;
		fvec2				_sMDelta;
		e_input_flags::type	_eParams;
		e_key_state::type	_aKeys[ e_key::_COUNT ];
		int16				_iDeltaWheel;
		char				_cLastChar;
		wchar				_wLastChar;
		e_text_input_mode	_eTextInput;

	protected:
		void ProcessMessage(const TWindowEvent &sEvent);

	public:
		CInput();
		~CInput();

		bool	Init(CWindow *pWnd);
		void	Destroy();

		//bool	SetParams(e_input_flags::type eFlags);
		void	Update();	// before check input
		void	Reset();	// after check input

		//void	ClipCursor(bool bClip)			const;

		bool	IsKey(e_key::type eKey)			const;
		bool	IsKeyClick(e_key::type eKey);
		bool	IsKeyUp(e_key::type eKey);

		bool	IndexToKey(uint16 uIndex, e_key::type &eKey) const;

		void	GetPressedKeys(key_array_t &aKeys)	const;
		void	GetPressedMB(key_array_t &aMButtons)const;
		void	GetAllPressed(key_array_t &aKeys)	const;

		char	GetCharA()		const				{ return _cLastChar; }
		wchar	GetCharW()		const				{ return _wLastChar; }

		void	BiginTextInputA();
		void	BeginTextInputW();
		void	EndTextInputA();
		void	EndTextInputW();

		bool	IsTextInputA()	const				{ return EnumCmp( _eTextInput, TIM_ASCII ); }
		bool	IsTextInputW()	const				{ return EnumCmp( _eTextInput, TIM_UNICODE ); }

		int		GetDeltaWheel() const				{ return _iDeltaWheel; }

		e_key_state::type	GetKeyState(e_key::type eKey)	const;
		uni_c_string 		GetKeyName(e_key::type eKey)	const;

		const ivec2		&	GetMousePos()	const	{ return _sMPos; }
		const fvec2		&	GetMouseDelta()	const	{ return _sMDelta; }
		const string	&	GetTextA()		const	{ return _sText; }
		const wstring	&	GetTextW()		const	{ return _sWText; }

		void CursorToCenter() const;
		void SetCursorPos(const fvec2 &vPos) const;

		// OSAPI //
		bool OS_MouseButtonState(e_key::type eMB, bool bPressed) const;
		bool OS_MouseMove(const ivec2 &vDelta) const;
		bool OS_MouseWheelState(int iDelta) const;
		bool OS_KeyState(e_key::type eKey, bool bPressed) const;
		bool OS_SetWChar(wchar c) const;
	};


	
	UX_ENUM_BIT_OPERATIONS( CInput::e_text_input_mode );


/*
=================================================
	возвращает состояние клавиши клавиатуры
=================================================
*/
	inline bool CInput::IsKey(e_key::type eKey) const
	{
		ASSUME( eKey < e_key::_COUNT );
		return ( _aKeys[eKey] > e_key_state::UP );
	}

	inline e_key_state::type CInput::GetKeyState(e_key::type eKey) const
	{
		ASSUME( eKey < e_key::_COUNT );
		return _aKeys[eKey];
	}

/*
=================================================
	возвращает состояние клавиши,
	если нажата, то возвращает true, но следующие
	запрос вернет false, пока клавишу не нажмут снова
=================================================
*/
	inline bool CInput::IsKeyClick(e_key::type eKey)
	{
		ASSUME( eKey < e_key::_COUNT );
		return ( _aKeys[eKey] == e_key_state::DOWN and (_aKeys[eKey] = e_key_state::PRESSED) );
	}
	
/*
=================================================
	возвращает состояние клавиши,
	если была отпущена, то возвращает true, но
	следующий запрос вернет false
=================================================
*/
	inline bool CInput::IsKeyUp(e_key::type eKey)
	{
		ASSUME( eKey < e_key::_COUNT );
		return ( _aKeys[eKey] == e_key_state::UP and !(_aKeys[eKey] = e_key_state::NOT_PRESSED) );
	}

/*
=================================================
	включить/выключить сохранение текста
=================================================
*/
	inline void CInput::BiginTextInputA()
	{
		if ( !IsTextInputA() ) {
			_eTextInput |= TIM_ASCII;
			_sText.SetLength(0);
		}
	}

	inline void CInput::BeginTextInputW()
	{
		if ( !IsTextInputW() ) {
			_eTextInput |= TIM_UNICODE;
			_sWText.SetLength(0);
		}
	}

	inline void CInput::EndTextInputA()
	{
		_eTextInput &= ~TIM_ASCII;
	}

	inline void CInput::EndTextInputW()
	{
		_eTextInput &= ~TIM_UNICODE;
	}

/*
=================================================
	возвращает название кнопки клавиатуры
=================================================
*/
	inline uni_c_string  CInput::GetKeyName(e_key::type eKey) const
	{
		if ( eKey >= e_key::_COUNT )	return null;
		else							return e_key::pNames[eKey];
	}
	
/*
=================================================
	возвращает все нажатые кнопки клавиатуры
=================================================
*/
	inline void CInput::GetPressedKeys(key_array_t &aKeys) const
	{
		aKeys.SetCount(0);

		for (e_key::type i = e_key::_KEY_BEGIN; i < e_key::_COUNT; ++i) {
			if ( IsKey( i ) )
				aKeys.PushBack( i );
		}
	}
	
/*
=================================================
	возвращает все нажатые кнопки мыши
=================================================
*/
	inline void CInput::GetPressedMB(key_array_t &aMButtons) const
	{
		aMButtons.SetCount(0);

		for (e_key::type i = e_key::type(0); i < e_key::_MB_COUNT; ++i) {
			if ( IsKey( i ) )
				aMButtons.PushBack( i );
		}
	}
	
/*
=================================================
	возвращает все нажатые кнопки мыши и клавиатуры
=================================================
*/
	inline void CInput::GetAllPressed(key_array_t &aKeys) const
	{
		aKeys.SetCount(0);

		for (e_key::type i = e_key::type(0); i < e_key::_COUNT; ++i) {
			if ( IsKey( i ) )
				aKeys.PushBack( i );
		}
	}

/*
=================================================
	возвращает код кнопки по индексу
=================================================
*/
	inline bool CInput::IndexToKey(uint16 uIndex, e_key::type &eKey) const
	{
		if ( uIndex < CountOf( e_key::aKeyIndices ) )
		{
			eKey = e_key::aKeyIndices[ uIndex ];
			return true;
		}
		return false;
	}

}	// UXTypesExt

	UX_ENUM_META_OBJECT( UXTypesExt::e_input_flags::type );
	UX_ENUM_META_OBJECT( UXTypesExt::e_key_state::type );
	
//-------------------------------------------------------------------

}	// UX_STL

#endif	// _UXS_INPUT_SYSTEM_H